package com.ybear.mvp.util;

import android.app.Activity;
import android.os.Build;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;

import com.ybear.mvp.annotations.Autowired;
import com.ybear.mvp.annotations.BindFragment;
import com.ybear.mvp.annotations.BindView;
import com.ybear.mvp.annotations.Model;
import com.ybear.mvp.annotations.ModelType;
import com.ybear.mvp.annotations.Presenter;
import com.ybear.mvp.model.ModelManage;
import com.ybear.mvp.model.MvpModel;
import com.ybear.mvp.presenter.MvpPresenter;
import com.ybear.mvp.view.MvpViewable;
import com.ybear.mvp.view.fragment.MvpFragment;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.NoSuchElementException;

public class MvpAnn {
    /**
     * 回调构建的值，返回 null 时跳过构建
     */
    public interface CallValue { Object value(Object obj, @NonNull Field f); }

    /**
     * 类中成员变量是否拥有指定注解{@link Annotation}，并且当前变量继承了指定父类
     * @param f     查询的类{@link Field}
     * @param ann   设置的注解
     * @param cls   继承的父类
     * @return      是否拥有
     */
    private static boolean isAnnAndSuper(@NonNull Field f, @Nullable Annotation ann,
                                         @NonNull Class<?> cls) {
        if( ann == null ) return false;
        //获取当前类型继承的父类型
        Class<?> tCls = f.getType();
        //设置了指定的注解，但没有继承指定父类
        return cls.equals( MvpUtil.findRootSuperclass( tCls ) ) || cls.equals( tCls );
    }

    /**
     * 类中成员变量是否拥有{@link Model}
     * @param f     成员变量
     * @return      是否拥有
     */
    public static boolean isAnnModel(@NonNull Field f) {
        return isAnnAndSuper(f, f.getAnnotation( Model.class ), MvpModel.class);
    }

    /**
     * 类中成员变量是否拥有{@link Presenter}
     * @param f     成员变量
     * @return      是否拥有
     */
    public static boolean isAnnPresenter(@NonNull Field f) {
        return isAnnAndSuper(f, f.getAnnotation( Presenter.class ), MvpPresenter.class);
    }

    /**
     * 当前类或者类中的成员变量是否拥有{@link Autowired}
     * @param f     成员变量
     * @return      是否拥有
     */
    public static boolean isAnnAutowired(@NonNull Field f) {
        return f.getAnnotation( Autowired.class ) != null;
    }

    /**
     * 实例当前类中所有成员变量
     * 成员变量必须拥有{@link Model}、{@link Autowired}、{@link Presenter} 其中之一。
     * @param obj   当前类，this
     */
    public static void instanceMvpAnn(@NonNull Object obj) {
        instanceValClass(obj, (o1, f) -> {
            //是否拥有Model
            if( isAnnModel( f ) ) return createModel( o1, f );
            //是否拥有Autowired
            if( isAnnAutowired( f ) ) return MvpUtil.newClass( f.getType() );
            //是否拥有Presenter
            return isAnnPresenter( f ) ? createPresenter( o1, f ) : null;
        });
    }

    /**
     * 根据类型创建Model
     * @param obj   持有者，一般为this
     * @param f     成员变量
     * @return      Model实例
     */
    @Nullable
    private static Object createModel(@NonNull Object obj, @NonNull Field f) {
        Model ann = f.getAnnotation( Model.class );
        if( ann == null ) return null;
        switch ( ann.value() ) {
            case ModelType.SHARED:        //使用管理器实现model共享
                return ModelManage.get().getCreateModel( obj, f.getType() );
            case ModelType.NEW_MODEL:     //实例新的Model，不参与共享
                return MvpUtil.newClass( f.getType() );
            default:
                return null;
        }
    }

    /**
     * 创建Presen
     * @param obj   持有者，一般为this
     * @param f     成员变量
     * @return      Presen实例
     */
    @Nullable
    private static Object createPresenter(@NonNull Object obj, @NonNull Field f) {
        /* 传入的V可能是实现类（this）或者V接口，所以先假设它是实现类获取它的class，
        然后查找这个类是否存在MvpViewable.class，有的话它就是实现类，然后对它重新赋值 */
        Class<?> viewCls = MvpUtil.findHoldClass( obj, MvpViewable.class );
        if( viewCls == null ) {
            throw new NoSuchElementException("Your Activity or Fragment need to implement the MvpViewable interface.");
        }
        //设置参数
        return MvpUtil.newClass(
                f.getType(),
                new Class[] { viewCls },
                obj
        );
    }

    public static void instanceBindAll(@NonNull Object obj) {
        instanceBindView( obj );
        instanceBindFragment( obj );
    }
    /**
     实例调用类中注解 {@link BindView} 的 {@link Activity}、
     {@link Fragment}、{@link android.app.Fragment} 的成员变量
     @param obj   调用类（this）
     */
    public static void instanceBindView(@NonNull Object obj) {
        instanceValClass( obj, (o1, f ) -> {
            BindView ann = f.getAnnotation( BindView.class );
            int id;
            if( ann == null || ( id = ann.value() ) == 0 ) return null;
            if( o1 instanceof Activity ) {
                return ((Activity) o1).findViewById( id );
            }
            if( o1 instanceof Fragment ) {
                View v = ((Fragment) o1).getView();
                return v != null ? v.findViewById( id ) : null;
            }
            if( o1 instanceof android.app.Fragment ) {
                View v = ((android.app.Fragment) o1).getView();
                return v != null ? v.findViewById( id ) : null;
            }
            return null;
        });
    }

    /**
     实例调用类中注解 {@link BindView} 的 {@link MvpFragment} 和
     {@link com.ybear.mvp.view.MvpFragment} 的成员变量
     @param obj     调用类（this）
     */
    public static void instanceBindFragment(@NonNull Object obj) {
        instanceValClass( obj, (o1, f ) -> {
            BindFragment ann = f.getAnnotation( BindFragment.class );
            int id;
            if( o1 == null || ann == null || ( id = ann.value() ) == 0 ) return null;
            if( o1 instanceof FragmentActivity ) {
                return ((FragmentActivity) o1).getSupportFragmentManager().findFragmentById( id );
            }
            if( o1 instanceof Activity ) {
                return ((Activity) o1).getFragmentManager().findFragmentById( id );
            }
            if( o1 instanceof Fragment ) {
                return ((Fragment) o1).getChildFragmentManager().findFragmentById( id );
            }

            if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 ) {
                if( o1 instanceof android.app.Fragment ) {
                    return ((android.app.Fragment) o1).getChildFragmentManager().findFragmentById( id );
                }
            }
            return null;
        });
    }

    /**
     * 实例条件范围内的成员变量
     * @param obj   实例的泛型，一般为：this
     * @param call  回调条件。返回null时跳过当前变量
     */
    public static void instanceValClass(@NonNull Object obj, @Nullable CallValue call) {
        Class<?> cls = obj.getClass();
        //实例所有父类的成员变量
        instanceSuperclassValClassAll( obj, cls, call );
        //实例当前类的成员变量
        setFields( obj, cls.getDeclaredFields(), call );
    }

    /**
     * 实例所有继承类下条件范围内的成员变量
     * @param obj   实例的泛型，一般为：this
     * @param cls   泛型的类。{@link #getClass()}
     * @param call  回调条件。返回null时跳过当前变量
     */
    private static void instanceSuperclassValClassAll(@NonNull Object obj, @NonNull Class<?> cls,
                                                       @Nullable CallValue call) {
        Class<?> superCls = cls.getSuperclass();
        if( superCls == null ) return;
        instanceSuperclassValClassAll( obj, superCls, call );
        setFields( obj, superCls.getDeclaredFields(), call );
    }

    private static void setFields(@NonNull Object obj, Field[] fs, @Nullable CallValue call) {
        //遍历当前类中的所有变量，包括了私有变量
        for( Field f : fs ) {
            Object val;
            if( call == null || ( val = call.value( obj, f ) ) == null ) continue;
            //允许被访问
            f.setAccessible( true );
            try {
                f.set( obj, val );
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}
